import hmac
import requests
import urllib.parse
import sys
import json
from coin.exchange.base.kr_rest.auth_base import AuthKey
from coin2.api_client.common import (SyncQueryer)
from enum import Enum

class Side(Enum):
  BUY = "Buy"
  SELL = "Sell"

class OrderType(Enum):
  MARKET = "Market"
  LIMIT = "Limit"

class TimeInForce(Enum):
  GOOD_TILL_CANCEL = "GoodTillCancel"
  IMMEDIATE_OR_CANCEL = "ImmediateOrCancel"
  FILL_OR_KILL = "FillOrKill"
  POST_ONLY = "PostOnly"

BYBIT_API_URL = "https://api.bybit.com"

def _get_server_timestamp():
  path = BYBIT_API_URL + '/v2/public/time'
  r = requests.get(path)
  return int(float(r.json()['time_now']) * 1000)

class BybitAuth(requests.auth.AuthBase):
  def __init__(self, key):
    self._key = AuthKey.from_file(key)

  def _get_signed_params(self, param_str):
    return hmac.new(self._key._secret_key_bytes, param_str.encode(), digestmod="sha256").hexdigest()

  def __call__(self, req: requests.PreparedRequest):
    timestamp = _get_server_timestamp()

    recv_window = 5000

    parsed = urllib.parse.urlparse(req.url)

    param_str = str(timestamp) + self._key.access_key + str(recv_window) + (parsed.query if req.method == "GET" else req.body.decode())
    sign = self._get_signed_params(param_str)

    req.headers.update({
      "X-BAPI-SIGN": sign,
      "X-BAPI-API-KEY": self._key.access_key,
      "X-BAPI-TIMESTAMP": timestamp,
      "X-BAPI-RECV-WINDOW": recv_window,    
    })

    return req

class BybitPrivateClient():
  def __init__(self, key, mea, ip=None):
    self._key = key
    self._mea = mea
    self._auth = BybitAuth(key)
    self._url = BYBIT_API_URL
    self._query = SyncQueryer(api_host=self._url, auth=self._auth, ip=ip)

  def get_open_order(self, settle_coin: str):
    assert self._mea == "Futures.Bybit.v3"
    assert settle_coin, "settle_coin must be not None and not empty!"

    path = "/contract/v3/private/order/unfilled-orders"

    method = "GET"
    params = {
      "settleCoin": settle_coin
    }
    response = self._query.query(
        method=method, params=params, path=path)
    return response

  def get_position(self, settle_coin: str):
    assert self._mea == "Futures.Bybit.v3"

    path = "/contract/v3/private/position/list"

    method = "GET"
    params = {
        "settleCoin": settle_coin
    }
    response = self._query.query(
        method=method, params=params, path=path)
    return response

  def get_balance(self):
    assert self._mea == "Spot.Bybit.v3"

    path = "/spot/v3/private/account"

    method = "GET"
    params = {
    }
    response = self._query.query(
        method=method, params=params, path=path)
    return response

  def get_risk_limit_list(self):
    path = "/derivatives/v3/public/risk-limit/list"

    method = "GET"
    params = {
    }
    response = self._query.query(
        method=method, params=params, path=path)
    return response

  def submit_order(
      self,
      symbol: str,
      side: Side,
      order_type: OrderType,
      qty: str,
      time_in_force: TimeInForce,
      price: str = None,
      order_link_id: str = None,
    ):
    assert self._mea == "Futures.Bybit.v3"
    assert symbol, "symbol must be not None and not empty!"
    assert side, "side must be not None!"
    assert order_type, "order_type must be not None!"
    assert qty, "qty must be not None and not empty!"
    assert time_in_force, "time_in_force must be not None!"

    path = "/contract/v3/private/order/create"

    method = "POST"
    json = {
      "symbol": symbol,
      "side": side.value,
      "orderType": order_type.value,
      "qty": qty,
      "timeInForce": time_in_force.value,
      "price": price,
      "orderLinkId": order_link_id,
    }
    response = self._query.query(
        method=method, json=json, path=path)
    return response

if __name__ == "__main__":
  key_path = sys.argv[1]
  config = {"key_path": key_path, "mea": "Futures.Bybit.v2", "symbols":["BTCUSD", "ETHUSD"]}
  # order, resp = detecotor_open_order(config)
  # print(json.dumps(order, indent=2))
  
  coincheck = BybitPrivateClient(key_path, "Futures.Bybit.v3")
  resp = coincheck.get_open_order("USDT")
  #resp = coincheck.get_risk_limit_list()
  #resp = coincheck.get_position("USDT")
  # resp = coincheck.submit_order("BTCUSDT", Side.SELL, OrderType.LIMIT, "1", TimeInForce.GOOD_TILL_CANCEL, "40000", "tianqiangtest01")
  resp = resp.json()
  print(json.dumps(resp, indent=2))
